/*
* File: CFdEventDevPoll.cpp
* Copyright (c) 2010. Silviu Caragea <silviu@intelliproject.net>
* Created on October 14, 2010, 12:32 AM
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*  1.Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*  2.Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*  3.All advertising materials mentioning features or use of this software
*    must display the following acknowledgement:
*       This product includes software developed by the University of
*       California, Berkeley and its contributors.
*  4.Neither the name of the University nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "CFdEventDevPoll.h"

#ifdef USE_SOLARIS_DEVPOLL

namespace litevent
{


CFdEventDevPoll::CFdEventDevPoll (size_t maxfds):IFdEventInterface(maxfds)
{
    this->devPollFds = (pollfd*)malloc(sizeof(*this->devPollFds) * this->maxfds);

    /*@todo: probably this can be replaced with reset :)*/

    if ((this->devPollFd = open("/dev/poll", O_RDWR)) < 0)
    {
        util::CLogger::sharedLogger()->WriteFormated("%s.%d: CFdEventDevPoll::CFdEventDevPoll opening /dev/poll failed: %s ", __FILE__, __LINE__,strerror(errno));
	throw util::CException("CFdEventDevPoll::CFdEventDevPoll opening /dev/poll failed",1) ;
    }

}

CFdEventDevPoll::~CFdEventDevPoll ()
{
    if(this->devPollFds)
        free(this->devPollFds);
    
    if(this->devPollFd !=-1)
        close(this->devPollFd);
}

int CFdEventDevPoll::setEvent(int fd_node , int fd, int events)
{
    struct pollfd pfd;
    int shouldAdd = 0;
    int pollEvents = 0;

    if (events & FDEVENT_IN)
        pollEvents |= POLLIN;

    if (events & FDEVENT_OUT)
        pollEvents |= POLLOUT;

    if (fd_node == -1)
      shouldAdd = 1;

    pfd.fd = fd;
    pfd.events = pollEvents;
    pfd.revents = 0;

    if (write(this->devPollFd, &pfd, sizeof(pfd)) == -1)
    {
        util::CLogger::sharedLogger()->WriteFormated("%s.%d: CFdEventDevPoll::setEvent [set] write failed: fd:%d, message: %s ", __FILE__, __LINE__,fd,strerror(errno));
        return -1;
    }

    return fd;
}

int CFdEventDevPoll::deleteEvent(int fd_node, int fd )
{
    struct pollfd pfd;

    if (fd_node < 0)
      return 0;

    pfd.fd = fd;
    pfd.events = POLLREMOVE;
    pfd.revents = 0;

    if (write(this->devPollFd, &pfd, sizeof(pfd)) == -1)
    {
        util::CLogger::sharedLogger()->WriteFormated("%s.%d: CFdEventDevPoll::deleteEvent [del] write failed: fd:%d, message: %s ", __FILE__, __LINE__,fd,strerror(errno));
	return 0;
    }

    return 1;
}


int CFdEventDevPoll::poll(int timeoutMs)
{
    struct dvpoll devPoll;

    devPoll.dp_timeout = timeoutMs;
    devPoll.dp_nfds = this->maxfds - 1;
    devPoll.dp_fds = this->devPollFds;

    return ioctl(this->devPollFd, DP_POLL, &devPoll);
}

int CFdEventDevPoll::getEventRevent(size_t node)
{
    int pollRevents = this->devPollFds[node].revents;
    
    int r = 0;
    
    if (pollRevents & POLLIN)
        r |= FDEVENT_IN;

    if (pollRevents & POLLOUT)
        r |= FDEVENT_OUT;

    if (pollRevents & POLLERR)
        r |= FDEVENT_ERR;

    if (pollRevents & POLLHUP)
        r |= FDEVENT_HUP;

    if (pollRevents & POLLNVAL)
        r |= FDEVENT_NVAL;

    if (pollRevents & POLLPRI)
        r |= FDEVENT_PRI;

    return r;
}

int CFdEventDevPoll::getEventFileDescriptor(size_t node)
{
    return this->devPollFds[node].fd;
}

int CFdEventDevPoll::getEventNextFileDescriptor(int node)
{
    return (node < 0) ? 0 : node + 1;
}

int CFdEventDevPoll::reset()
{
    if ((this->devPollFd = open("/dev/poll", O_RDWR)) < 0)
    {
        util::CLogger::sharedLogger()->WriteFormated("%s.%d: CFdEventDevPoll::reset opening /dev/poll failed: %s ", __FILE__, __LINE__,strerror(errno));
        return -1;
    }
  
    if (fcntl(this->devPollFd, F_SETFD, FD_CLOEXEC) < 0)
    {
        util::CLogger::sharedLogger()->WriteFormated("%s.%d: CFdEventDevPoll::reset fcntl /dev/poll fd failed: %s ", __FILE__, __LINE__,strerror(errno));
	close(this->devPollFd);
	return -1;
    }
    
    return 0;
}

}
#endif
